package com.shigen.file.aspect;

import cn.hutool.core.util.StrUtil;
import com.shigen.file.annotation.RequestLimit;
import com.shigen.file.config.TokenUtil;
import com.shigen.file.exception.BusinessException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 自定义切面判定访问频率
 *
 * @author shigen.fu
 */
@Aspect
@Component
@Slf4j
public class RequestLimitAspect {

  @Resource
  private StringRedisTemplate stringRedisTemplate;
  @Resource
  private TokenUtil tokenUtil;

  @Before("@annotation(com.shigen.file.annotation.RequestLimit)")
  public void requestLimitIntercept(JoinPoint joinPoint) {
    HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();

    // 获取配置的过期时间
    RequestLimit annotation = method.getAnnotation(RequestLimit.class);
    int count = annotation.count();
    long time = annotation.time();

    String requestURI = request.getRequestURI();
    String token = request.getHeader("token");
    if (StrUtil.isBlank(requestURI) || StrUtil.isBlank(token)) {
      throw new BusinessException("用户的token获取失败");
    }
    String redisLimitKey = requestURI + ":" + tokenUtil.parseTokenWithTimestamp(token);
    if (!stringRedisTemplate.opsForValue().setIfAbsent(redisLimitKey, "1", time, TimeUnit.SECONDS)) {
      // 获得count值再次比较
      int timesRecord = Integer.parseInt(stringRedisTemplate.opsForValue().get(redisLimitKey));
      if (timesRecord >= count) {
        throw new BusinessException("请求次数过多");
      } else {
        timesRecord++;
        stringRedisTemplate.opsForValue().set(redisLimitKey, String.valueOf(timesRecord), time, TimeUnit.SECONDS);
      }
    }

  }


}
